/*
 * Copyright (C) 2008 Universidade Federal de Campina Grande
 *  
 * This file is part of OurGrid. 
 *
 * OurGrid is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free 
 * Software Foundation, either version 3 of the License, or (at your option) 
 * any later version. 
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public License 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.ourgrid.acceptance.util.peer;

import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.easymock.classextension.EasyMock;
import org.ourgrid.acceptance.util.PeerAcceptanceUtil;
import org.ourgrid.common.interfaces.control.PeerControl;
import org.ourgrid.common.interfaces.control.PeerControlClient;
import org.ourgrid.common.interfaces.management.PeerManager;
import org.ourgrid.common.spec.main.DescriptionFileCompile;
import org.ourgrid.common.spec.worker.WorkerSpec;
import org.ourgrid.matchers.ControlOperationResultMatcher;
import org.ourgrid.matchers.SaveAccountingRepetitionRunnableMatcher;
import org.ourgrid.peer.PeerComponent;
import org.ourgrid.peer.PeerConstants;
import org.ourgrid.peer.business.controller.accounting.AccountingConstants;

import br.edu.ufcg.lsd.commune.container.ObjectDeployment;
import br.edu.ufcg.lsd.commune.container.logging.CommuneLogger;
import br.edu.ufcg.lsd.commune.container.servicemanager.actions.RepetitionRunnable;
import br.edu.ufcg.lsd.commune.context.ModuleContext;
import br.edu.ufcg.lsd.commune.identification.ContainerID;
import br.edu.ufcg.lsd.commune.identification.DeploymentID;
import br.edu.ufcg.lsd.commune.network.signature.SignatureProperties;
import br.edu.ufcg.lsd.commune.testinfra.AcceptanceTestUtil;

public class Req_010_Util extends PeerAcceptanceUtil {

	public Req_010_Util(ModuleContext context) {
		super(context);
	}

	/**
	 * Sets workers in a peer according to a SDF file
	 * @param sdfFilePath The SDF file path
	 * @param context 
	 * @throws Exception 
	 */
	public void setWorkers(PeerComponent component, String sdfFilePath, ModuleContext context) throws Exception {
		
		/*PeerSyncApplicationClient componentClient = new PeerSyncApplicationClient(context);
		componentClient.putOnQueue(new ControlOperationResult());

		componentClient.setWorkers(DescriptionFileCompile.compileSDF(TEST_FILES_PATH + sdfFilePath));*/
		
		setWorkers(component, DescriptionFileCompile.compileSDF(TEST_FILES_PATH + sdfFilePath));
	}
	
	public void setClassAdsWorkers(PeerComponent component, String sdfFilePath, ModuleContext context) throws Exception {
		
		/*PeerSyncApplicationClient componentClient = new PeerSyncApplicationClient(context);
		componentClient.putOnQueue(new ControlOperationResult());

		componentClient.setWorkers(DescriptionFileCompile.compileSDF(TEST_FILES_PATH + sdfFilePath));*/
		
		setWorkers(component, DescriptionFileCompile.compileNewSDF(TEST_FILES_PATH + sdfFilePath));
	}
	
	/**
	 * Sets workers in a peer according to their <code>WorkerSpec</code>
	 * @param workers Attributes of the workers to be set
	 */
	public void setWorkers(PeerComponent component, List<WorkerSpec> workers) {
		// Get peer control
		ObjectDeployment peerControlDeployment = getPeerControlDeployment();
		
		// Set the workers with the peer control public key
		setWorkers(component, workers, peerControlDeployment.getDeploymentID().getPublicKey());
	}

	/**
	 * Sets workers in a peer according to their <code>WorkerSpec</code>, setting the sender public key
	 * @param workers Attributes of the workers to be set
	 * @param senderPubKey The sender public key
	 */
	public void setWorkers(PeerComponent component, List<WorkerSpec> workers, String senderPubKey) {
	
	    // Create client mock
	    PeerControlClient peerControlClientMock = EasyMock.createMock(PeerControlClient.class);
	    
	    //publish client mock
	    DeploymentID pccID = new DeploymentID(new ContainerID("peerClient", "peerClientServer", "broker", senderPubKey), "broker");
	    AcceptanceTestUtil.publishTestObject(component, pccID, peerControlClientMock, PeerControlClient.class);
	
	    // Record mock behavior
	    peerControlClientMock.operationSucceed(ControlOperationResultMatcher.noError());
	    EasyMock.replay(peerControlClientMock);
	
	    // Get peer control
	    PeerControl peerControl = getPeerControl();
		ObjectDeployment peerControlDeployment = getPeerControlDeployment();
	
	    // defining workers on peer
		AcceptanceTestUtil.setExecutionContext(component, peerControlDeployment, pccID);
		//AcceptanceTestUtil.setExecutionContext(component, peerControlDeployment, senderPubKey);
	    peerControl.setWorkers(peerControlClientMock, workers);
	
	    EasyMock.verify(peerControlClientMock);
	}
	
	public void setWorkersWithWrongPubKey(PeerComponent component, List<WorkerSpec> workers, String senderPubKey) {
		// Get the logger mock
		CommuneLogger oldLogger = component.getLogger();
		CommuneLogger newLogger = EasyMock.createMock(CommuneLogger.class);
		component.setLogger(newLogger);
		
		// Create client mock
	    PeerControlClient peerControlClientMock = EasyMock.createMock(PeerControlClient.class);
	
	    // Get peer control
	    PeerControl peerControl = getPeerControl();
		ObjectDeployment peerControlDeployment = getPeerControlDeployment();
		
	    // Verifies if this is my public key
	    if (senderPubKey.equals(peerControlDeployment.getDeploymentID().getPublicKey())) {
			peerControlClientMock.operationSucceed(ControlOperationResultMatcher.noError());
		} else {
			newLogger.warn("An unknown entity tried to set the workers. Only the local modules can perform this operation." +
					" Unknown entity public key: [" + senderPubKey + "].");
		}
	    
	    EasyMock.replay(peerControlClientMock);
		EasyMock.replay(newLogger);
	
	    // defining workers on peer
		AcceptanceTestUtil.setExecutionContext(component, peerControlDeployment,senderPubKey);
	    peerControl.setWorkers(peerControlClientMock, workers);
	
	    EasyMock.verify(peerControlClientMock);
	    EasyMock.verify(newLogger);
	    
	    component.setLogger(oldLogger);
	}

	
	/**
     * Sets workers in a peer according to their <code>WorkerSpec</code>
     * 
     * @param workers
     *            Attributes of the workers to be set
     */
    public void setWorkersTags(PeerComponent component, List<WorkerSpec> workers) {
            // Get peer control
            ObjectDeployment peerControlDeployment = getPeerControlDeployment();

            // Set the workers with the peer control public key
            setWorkersTags(component, workers, peerControlDeployment.getDeploymentID().getPublicKey());
    }

    /**
     * Sets workers in a peer according to their <code>WorkerSpec</code>,
     * setting the sender public key
     * 
     * @param workers
     *            Attributes of the workers to be set
     * @param senderPubKey
     *            The sender public key
     */
    public void setWorkersTags(PeerComponent component, List<WorkerSpec> workers, String senderPubKey) {

            // Create client mock
            PeerControlClient peerControlClientMock = EasyMock.createMock(PeerControlClient.class);

            // publish client mock
            DeploymentID pccID = new DeploymentID(new ContainerID("peerClient", "peerClientServer", "broker"), "broker");
            
        	AcceptanceTestUtil.publishTestObject(component, pccID, peerControlClientMock, PeerControlClient.class);
        

            // Record mock behavior
            peerControlClientMock.operationSucceed(ControlOperationResultMatcher.noError());
            EasyMock.replay(peerControlClientMock);

            // Get peer control
            PeerControl peerControl = getPeerControl();
            ObjectDeployment peerControlDeployment = getPeerControlDeployment();

            // defining workers on peer
            AcceptanceTestUtil.setExecutionContext(component, peerControlDeployment, senderPubKey);
            peerControl.addAnnotationsWorkers( peerControlClientMock, workers);

            EasyMock.verify(peerControlClientMock);
    }
	
	
	/**
	 * Creates mocks for timer and logger and starts the peer
	 * @param component The peer component to be started
	 * @return The peer component
	 */
	public PeerComponent startPeer(PeerComponent component) {
		ObjectDeployment peerControlDeployment = getPeerControlDeployment();
		
		return startPeer(component, peerControlDeployment.getDeploymentID().getPublicKey());
	}
	
	/**
	 * Creates mocks for timer and logger and starts the peer
	 * @param component The peer component to be started
	 * @param senderPublicKey The sender public key
	 * @return The peer component
	 */
	@SuppressWarnings("unchecked")
	public PeerComponent startPeer(PeerComponent component, String senderPublicKey) {
		ScheduledExecutorService oldTimer = component.getTimer();
		ScheduledExecutorService newTimer = EasyMock.createMock(ScheduledExecutorService.class);
		component.setTimer(newTimer);
		
		CommuneLogger logger = EasyMock.createMock(CommuneLogger.class);
		component.setLogger(logger);
				
		PeerControlClient peerControlClientMock = EasyMock.createMock(PeerControlClient.class);
		ObjectDeployment peerControlDeployment = getPeerControlDeployment();
		
		boolean rightPubKey = senderPublicKey.equals(peerControlDeployment.getDeploymentID().getPublicKey()); 
		if (rightPubKey) {
			peerControlClientMock.operationSucceed(ControlOperationResultMatcher.noError());
		} else {
			logger.warn("An unknown entity tried to start the Peer. Only the local modules can perform this operation." +
					" Unknown entity public key: [" + senderPublicKey + "].");
			EasyMock.replay(logger);
		}
		
		EasyMock.replay(peerControlClientMock);
		
		ScheduledFuture<?> future = EasyMock.createMock(ScheduledFuture.class);
		EasyMock.replay(future);
		
		PeerManager peerManager = (PeerManager) getPeerControl();
		
		RepetitionRunnable runnableSaveAcc = 
			new RepetitionRunnable(component.getContainer(), peerManager, PeerConstants.SAVE_ACCOUNTING_ACTION_NAME, null);
		
		RepetitionRunnable runnableUpdateAcc = 
			new RepetitionRunnable(component.getContainer(), peerManager, PeerConstants.UPDATE_PEER_UPTIME_ACTION_NAME, null);
		
		long frequenceAcc = AccountingConstants.RANKING_SAVING_FREQ;
		
		//timer.scheduleWithFixedDelay(runnableSaveAcc, frequenceAcc, frequenceAcc, TimeUnit.SECONDS);
		
		if (rightPubKey) {
			EasyMock.expect( (ScheduledFuture) newTimer.scheduleWithFixedDelay(SaveAccountingRepetitionRunnableMatcher.eqMatcher(runnableSaveAcc),
					EasyMock.eq(frequenceAcc), EasyMock.eq(frequenceAcc),
					EasyMock.same(TimeUnit.SECONDS))).andReturn(future);

			EasyMock.expect( (ScheduledFuture) newTimer.scheduleWithFixedDelay(SaveAccountingRepetitionRunnableMatcher.eqMatcher(runnableUpdateAcc),
					EasyMock.eq(frequenceAcc), EasyMock.eq(frequenceAcc),
					EasyMock.same(TimeUnit.SECONDS))).andReturn(future);
		}
		
		EasyMock.replay(newTimer);
		
		DeploymentID peerClientID = new DeploymentID(new ContainerID("broker", "broker", "broker", senderPublicKey),"broker");
		
		AcceptanceTestUtil.publishTestObject(component, peerClientID, peerControlClientMock, PeerControlClient.class);
		
		AcceptanceTestUtil.setExecutionContext(component, peerControlDeployment, senderPublicKey);
		peerManager.start(peerControlClientMock);
		
		EasyMock.verify(peerControlClientMock);
		EasyMock.verify(newTimer);
		EasyMock.verify(future);
		
		if (!rightPubKey) {
			EasyMock.verify(logger);
			EasyMock.reset(logger);
		}
		
		component.setTimer(oldTimer);
		
		return component;
	}

	/**
	 * Creates a peer component ands starts the peer
	 * @return The created peer component
	 * @throws Exception
	 */
	public PeerComponent startPeer() throws Exception {
		PeerComponent component = createPeerComponent(context);
		component = startPeer(component, context.getProperty(SignatureProperties.PROP_PUBLIC_KEY));
		application = component;
		return component;
	}

	/**
	 * Stops the peer verifying logging for NOF Ranking file saving
	 * @param component The peer component
	 */
	public void notNiceStopPeer(PeerComponent component) {
		ObjectDeployment peerControlDeployment = getPeerControlDeployment();
		
		if(peerControlDeployment == null) {
			return;
		}
		
		stopPeer(component, false, peerControlDeployment.getDeploymentID().getPublicKey());
	}

	/**
	 * Stops the peer without verifying logging
	 * @param component The peer component
	 */
	public void niceStopPeer(PeerComponent component) {
		if (component == null) {
			return;
		}
		
		ObjectDeployment deployment = getPeerControlDeployment();
		
		if(deployment == null) {
			return;
		}
		
		stopPeer(component, true, deployment.getDeploymentID().getPublicKey());
	}
	
	/**
	 * Stops the peer verifying logging for NOF Ranking file saving
	 * @param component The peer component
	 * @param senderPubKey The publicKey of the component that asked the peer to stop
	 */
	public void notNiceStopPeer(PeerComponent component, String senderPubKey) {
		stopPeer(component, false, senderPubKey);
	}

	/**
	 * Stops the peer without verifying logging
	 * @param component The peer component
	 * @param senderPubKey The publicKey of the component that asked the peer to stop
	 */
	public void niceStopPeer(PeerComponent component, String senderPubKey) {
		stopPeer(component, true, senderPubKey);
	}


	private void stopPeer(PeerComponent component, boolean nice, String senderPubKey) {
		if(component == null) {
			return;
		}
		
		PeerControlClient peerControlClientMock = null;
		CommuneLogger oldLogger = component.getLogger();
		CommuneLogger newLogger = null;
		
		ObjectDeployment peerControlDeployment = getPeerControlDeployment();
		
		if (nice) {
			peerControlClientMock = EasyMock.createNiceMock(PeerControlClient.class);
			newLogger = EasyMock.createNiceMock(CommuneLogger.class);
		} else {
			peerControlClientMock = EasyMock.createMock(PeerControlClient.class);
			newLogger = EasyMock.createMock(CommuneLogger.class);
			
			if(senderPubKey.equals(peerControlDeployment.getDeploymentID().getPublicKey())) {
				newLogger.debug("Saving the Network of favours data.");
				peerControlClientMock.operationSucceed(ControlOperationResultMatcher.noError());
			} else {
				newLogger.warn("An unknown entity tried to stop the Peer. Only the local modules can perform this operation." +
						" Unknown entity public key: [" + senderPubKey + "].");
				newLogger.warn("An unknown sender tried to save the Network of favors data. This message was ignored." +
						" Sender public key: " + senderPubKey);
			}
			
			EasyMock.replay(peerControlClientMock);
			EasyMock.replay(newLogger);
		}
		
		component.setLogger(newLogger);
		
		// Stop the peer
		if (peerControlDeployment != null) {
			DeploymentID pccID = new DeploymentID(new ContainerID("pcc", "pcc", "peer", senderPubKey), "broker");
			
			AcceptanceTestUtil.publishTestObject(component, pccID, peerControlClientMock, PeerControlClient.class);
			AcceptanceTestUtil.setExecutionContext(component, peerControlDeployment, senderPubKey);
			getPeerControl().stop(false, false, peerControlClientMock);
		}
		
		if (!nice) {
			EasyMock.verify(peerControlClientMock);
			EasyMock.verify(newLogger);
		}
		
		component.setLogger(oldLogger);
	}
	
}